<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>swiper</title>
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <style>
        .swipe {
            overflow: hidden;
            position: relative;
            height: 200px;
            color: #fff;
            font-size: 30px;
            text-align: center;
        }
        .swipe-items-wrap {
            position: relative;
            overflow: hidden;
            height: 100%;
        }
        .swipe-items-wrap > div {
            position: absolute;
            transform: translateX(-100%);
            width: 100%;
            height: 100%;
            display: none;
        }
        .swipe-items-wrap > div.is-active {
            display: block;
            transform: none;
        }
        .swipe-item {
            line-height: 200px;
        }
        .slide1 {
            background-color: #0089dc;
            color: #fff;
        }
        .slide2 {
            background-color: #ffd705;
            color: #000;
        }
        .slide3 {
            background-color: #ff2d4b;
            color: #fff;
        }
    </style>
</head>
<body>
    <div class="swipe">
        <div class="swipe-items-wrap" id="wrap">
            <div class="swipe-item slide1">item 1</div>
            <div class="swipe-item slide2">item 2</div>
            <div class="swipe-item slide3">item 3</div>
        </div>
    </div>
    <script>
        var dragState = {};

        // variable
        var ready = false;
        var dragging = false;
        var userScrolling = false;
        var animating = false;
        var index = 0;
        var pages = [];
        var timer = null;
        var reInitTimer = null;
        var noDrag = false;
        var isDone = false;

        // options
        var speed = 300;
        var defaultIndex = 0;
        var auto = 3000;
        var continuous = true;
        var showIndicators = true;
        var noDragWhenSingle = true;
        var prevent = false;
        var stopPropagation = false;

        var children = [];
        var element = document.querySelector('#wrap');
        var childNodes = element.childNodes;
        children = [].slice.call(childNodes).filter(function(child) {
            return child.nodeType === 1;
        });

        function reInitPages() {
            noDrag = children.length === 1 && noDragWhenSingle;

            var aPages = [];
            var intDefaultIndex = Math.floor(defaultIndex);
            var defaultIndex = (intDefaultIndex >= 0 && intDefaultIndex < children.length) ? intDefaultIndex : 0;
            index = defaultIndex;

            children.forEach(function(child, index) {
                aPages.push(child);
                child.classList.remove('is-active');

                if (index === defaultIndex) {
                    child.classList.add('is-active');
                }
            });

            pages = aPages;
        }

        function translate(element, offset, speed, callback) {
            if (speed) {
                animating = true;
                element.style.webkitTransition = '-webkit-transform ' + speed + 'ms ease-in-out';
                setTimeout(function() {
                    element.style.webkitTransform = `translate3d(${offset}px, 0, 0)`;
                }, 50);

                var called = false;

                var transitionEndCallback = function() {
                    if (called) return;
                    called = true;
                    animating = false;
                    element.style.webkitTransition = '';
                    element.style.webkitTransform = '';
                    if (callback) {
                        callback.apply(this, arguments)
                    }
                }

                element.addEventListener('webkitTransitionEnd', transitionEndCallback);
                setTimeout(transitionEndCallback, speed + 100);
            } else {
                element.style.webkitTransition = '';
                element.style.webkitTransform = `translate3d(${offset}px, 0, 0)`;
            }
        }

        function doAnimate(towards, options) {
            if (children.length === 0) return;
            if (!options && children.length < 2) return;

            var prevPage, nextPage, currentPage, pageWidth, offsetLeft;
            // var speed = speed || 300;
            // var index = index;
            // var pages = pages;
            var pageCount = pages.length;

            if (!options) {
                pageWidth = element.clientWidth;
                currentPage = pages[index];
                prevPage = pages[index - 1];
                nextPage = pages[index + 1];
                if (continuous && pages.length > 1) {
                    if (!prevPage) {
                        prevPage = pages[pages.length - 1];
                    }

                    if (!nextPage) {
                        nextPage = pages[0];
                    }
                }

                if (prevPage) {
                    prevPage.style.display = 'block';
                    translate(prevPage, -pageWidth);
                }

                if (nextPage) {
                    nextPage.style.display = 'block';
                    translate(nextPage, pageWidth);
                }
            } else {
                prevPage = options.prevPage;
                currentPage = options.currentPage;
                nextPage = options.nextPage;
                pageWidth = options.pageWidth;
                offsetLeft = options.offsetLeft;
            }

            var newIndex;
            var oldPage = children[index];

            if (towards === 'prev') {
                if (index > 0) {
                    newIndex = index - 1;
                }
                if (continuous && index === 0) {
                    newIndex = pageCount - 1;
                }
            } else if (towards === 'next') {
                if (index < pageCount - 1) {
                    newIndex = index + 1;
                }
                if (continuous && index === pageCount - 1) {
                    newIndex = 0;
                }
            }

            var callback = function() {
                if (newIndex !== undefined) {
                    var newPage = children[newIndex];
                    oldPage.classList.remove('is-active');
                    newPage.classList.add('is-active');
                    index = newIndex
                }

                if (isDone) {
                    end();
                }

                if (prevPage) {
                    prevPage.style.display = '';
                }

                if (nextPage) {
                    nextPage.style.display = '';
                }
            }

            setTimeout(function() {
                if (towards === 'next') {
                    isDone = true;
                    before(currentPage);
                    translate(currentPage, -pageWidth, speed, callback);
                    if (nextPage) {
                        translate(nextPage, 0, speed)
                    }
                } else if (towards === 'prev') {
                    isDone = true;
                    before(currentPage);
                    translate(currentPage, pageWidth, speed, callback);
                    if (prevPage) {
                        translate(prevPage, 0, speed);
                    }
                } else {
                    isDone = true;
                    translate(currentPage, 0, speed, callback);
                    if (typeof offsetLeft !== 'undefined') {
                        if (prevPage && offsetLeft > 0) {
                            translate(prevPage, pageWidth * -1, speed);
                        }
                        if (nextPage && offsetLeft < 0) {
                            translate(nextPage, pageWidth, speed);
                        }
                    } else {
                        if (prevPage) {
                            translate(prevPage, pageWidth * -1, speed);
                        }

                        if (nextPage) {
                            translate(nextPage, pageWidth, speed);
                        }
                    }
                }
            }, 10);
        }

        function next() {
            doAnimate('next');
        }

        function prev() {
            doAnimate('prev');
        }

        function before() {
            // console.log('before', index);
        }

        function end() {
            // console.log('end', index);
        }

        function doOnTouchStart(event) {
            if (noDrag) return;

            var touch = event.touches[0];

            dragState.startTime = new Date();
            dragState.startLeft = touch.pageX;
            dragState.startTop = touch.pageY;
            dragState.startTopAbsolute = touch.clientY;

            dragState.pageWidth = element.offsetWidth;
            dragState.pageHeight = element.offsetHeight;

            var prevPage = children[this.index - 1];
            var dragPage = children[index];
            var nextPage = children[this.index + 1];


            if (continuous && pages.length > 1) {
                if (!prevPage) {
                    prevPage = children[children.length - 1];
                }

                if (!nextPage) {
                    nextPage = children[0];
                }
            }

            dragState.prevPage = prevPage ? prevPage : null;
            dragState.dragPage = dragPage ? dragPage : null;
            dragState.nextPage = nextPage ? nextPage : null;

            if (dragState.prevPage) {
                dragState.prevPage.style.display = 'block';
            }

            if (dragState.nextPage) {
                dragState.nextPage.style.display = 'block';
            }
        }

        function doOnTouchMove() {
            if (noDrag) return;

            var touch = event.touches[0];

            dragState.currentLeft = touch.pageX;
            dragState.currentTop = touch.pageY;
            dragState.currentTopAbsolute = touch.clientY;

            var offsetLeft = dragState.currentLeft - dragState.startLeft;
            var offsetTop = dragState.currentTopAbsolute - dragState.startTopAbsolute;

            var distanceX = Math.abs(offsetLeft);
            var distanceY = Math.abs(offsetTop);
            if (distanceX < 5 || ( distanceY >= 5 && distanceY >= 1.73 * distanceX )) {
                userScrolling = true;
                return;
            } else {
                userScrolling = false;
                event.preventDefault();
            }

            offsetLeft = Math.min(Math.max(-dragState.pageWidth + 1, offsetLeft), dragState.pageWidth - 1);

            var towards = offsetLeft < 0 ? 'next' : 'prev';

            if (dragState.prevPage && towards === 'prev') {
                translate(dragState.prevPage, offsetLeft - dragState.pageWidth);
            } 

            translate(dragState.dragPage, offsetLeft);

            if (dragState.nextPage && towards === 'next') {
                translate(dragState.nextPage, offsetLeft + dragState.pageWidth);
            }
        }

        function doOnTouchEnd() {
            if (noDrag) return;

            var dragDuration = new Date() - dragState.startTime;
            var towards = null;

            var offsetLeft = dragState.currentLeft - dragState.startLeft;
            var offsetTop = dragState.currentTop - dragState.startTop;
            var pageWidth = dragState.pageWidth;
            var pageCount = pages.length;

            if (dragDuration < 300) {
                var fireTap = Math.abs(offsetLeft) < 5 && Math.abs(offsetTop < 5);
                if (isNaN(offsetLeft) || isNaN(offsetTop)) {
                    fireTap = true;
                }
                if (fireTap) {
                    console.log('tap');
                }
            }

            if (dragDuration < 300 && dragState.currentLeft === undefined) return;

            if (dragDuration < 300 || Math.abs(offsetLeft) > pageWidth / 2) {
                towards = offsetLeft < 0 ? 'next' : 'prev';
            }

            if (!continuous) {
                if ((index === 0 && towards === 'prev') || (index === pageCount - 1 && towards === 'next')) {
                    towards = null;
                }
            }

            if (children.length < 2) {
                towards = null;
            }

            doAnimate(towards, {
                offsetLeft: offsetLeft, 
                pageWidth: dragState.pageWidth,
                prevPage: dragState.prevPage,
                currentPage: dragState.dragPage,
                nextPage: dragState.nextPage
            });

            dragState = {};
        }

        function initTimer() {
            if (auto > 0 && !timer) {
                timer = setInterval(function() {
                    if (!continuous && index >= pages.length - 1) {
                        return clearTimer();
                    }

                    if (!dragging && !animating) {
                        next();
                    }
                }, auto);
            }
        }

        function clearTimer() {
            clearInterval(timer);
            timer = null;
        }

        function mounted() {
            ready = true;

            // 设置定时器
            initTimer();

            // 初始化页面
            reInitPages();

            // 绑定事件
            element.addEventListener('touchstart', function(event) {
                if (prevent) event.preventDefault();
                if (stopPropagation) event.stopPropagation();
                if (animating) return;

                dragging = true;
                userScrolling = false;
                doOnTouchStart(event);
            });

            element.addEventListener('touchmove', function(event) {
                if (!dragging) return;
                if (timer) clearTimer();
                doOnTouchMove(event);
            });

            element.addEventListener('touchend', function(event) {
                if (userScrolling) {
                    dragging = false;
                    dragState = {};
                    return;
                }

                if (!dragging) return;
                initTimer();
                doOnTouchEnd(event);
                dragging = false;
            });
        }

        document.addEventListener('DOMContentLoaded', mounted);
    </script>
</body>
</html>